package com.oig.sys.workflow.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.oig.common.constants.CommonEnum;
import com.oig.common.exception.BadRequestException;
import com.oig.common.module.SysRequest;
import com.oig.sys.workflow.dao.ITbListenerObjectDao;
import com.oig.sys.workflow.dto.ListenerObjectDto;
import com.oig.sys.workflow.entity.TbListenerObject;
import com.oig.sys.workflow.service.ITbAutoRuleService;
import com.oig.sys.workflow.service.ITbListenerEventService;
import com.oig.sys.workflow.service.ITbListenerObjectService;
import com.oig.sys.workflow.vo.ListenerEventVo;
import com.oig.sys.workflow.vo.ListenerObjectVo;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * @author wuxl
 */
@Service
public class TbListenerObjectServiceImpl implements ITbListenerObjectService {

	private final ITbListenerObjectDao tbListenerObjectDao;
	private final ITbListenerEventService listenerEventService;
	private final ITbAutoRuleService autoRuleService;

	@Autowired
	public TbListenerObjectServiceImpl(ITbListenerObjectDao tbListenerObjectDao,
									   ITbListenerEventService listenerEventService,
									   ITbAutoRuleService autoRuleService){
		 this.tbListenerObjectDao = tbListenerObjectDao;
		 this.listenerEventService = listenerEventService;
		 this.autoRuleService = autoRuleService;
	}

	@Transactional
	@Override
	public ListenerObjectVo addListenerObject(ListenerObjectDto listenerObjectDto){
		TbListenerObject listenerObject = new TbListenerObject();
		BeanUtils.copyProperties(listenerObjectDto, listenerObject,"id");
		if (CommonEnum.FlagEnum.TRUE_FLAG.getCode().equals(listenerObjectDto.getReqType())){
			listenerObject.setStatus(CommonEnum.CommonStatusEnum.CHECKING.getCode());
		} else {
			listenerObject.setStatus(CommonEnum.CommonStatusEnum.INIT.getCode());
		}
		listenerObject.setLastUpdateName(listenerObject.getCreateUserName());
		this.tbListenerObjectDao.save(listenerObject);
		ListenerObjectVo objectVo = this.buildVo(listenerObject);
		List<ListenerEventVo> eventVoList=listenerEventService.addListenerEventList(listenerObject, listenerObjectDto.getListenerEventDtoList()) ;
		objectVo.setListenerEventVoList(eventVoList);
		return objectVo ;
	}

	private ListenerObjectVo buildVo(TbListenerObject listenerObject){
		ListenerObjectVo objectVo = new ListenerObjectVo();
		BeanUtils.copyProperties(listenerObject, objectVo);
		return objectVo;
	}

	@Transactional
	@Override
	public ListenerObjectVo updateListenerObject(ListenerObjectDto listenerObjectDto){
		TbListenerObject listenerObject = this.tbListenerObjectDao.getLOById(listenerObjectDto.getId()) ;
		if (!CommonEnum.CommonStatusEnum.INIT.getCode().equals(listenerObject.getStatus())
				&&!CommonEnum.CommonStatusEnum.UN_ENABLE.getCode().equals(listenerObject.getStatus())){
			throw new BadRequestException("非草拟或不通过下不能修改") ;
		}
		BeanUtils.copyProperties(listenerObjectDto, listenerObject,"id","createUserId","createUserName","createTime");
		if (CommonEnum.FlagEnum.FALSE_FLAG.getCode().equals(listenerObjectDto.getReqType())){
			listenerObject.setStatus(CommonEnum.CommonStatusEnum.INIT.getCode());
		} else {
			listenerObject.setStatus(CommonEnum.CommonStatusEnum.CHECKING.getCode());
		}
		this.tbListenerObjectDao.save(listenerObject);
		ListenerObjectVo objectVo = this.buildVo(listenerObject);
		List<ListenerEventVo> eventVoList=listenerEventService.updateListenerEventList(listenerObject, listenerObjectDto.getListenerEventDtoList());
		objectVo.setListenerEventVoList(eventVoList);
		return objectVo ;
	}

	@Transactional
	@Override
	public ListenerObjectVo auditListenerObject(ListenerObjectDto listenerObjectDto) {
		TbListenerObject listenerObject = this.tbListenerObjectDao.getLOById(listenerObjectDto.getId()) ;
		if (!CommonEnum.CommonStatusEnum.CHECKING.getCode().equals(listenerObject.getStatus())){
			throw new BadRequestException("非待审核状态") ;
		}
		if (CommonEnum.FlagEnum.TRUE_FLAG.getCode().equals(listenerObjectDto.getReqType())){
			listenerObject.setStatus(CommonEnum.CommonStatusEnum.OK.getCode());
		} else {
			listenerObject.setStatus(CommonEnum.CommonStatusEnum.UN_ENABLE.getCode());
		}
		listenerObject.setCheckName(listenerObjectDto.getCheckName());
		listenerObject.setCheckId(listenerObjectDto.getCheckId());
		listenerObject.setCheckRemark(listenerObjectDto.getCheckRemark());
		listenerObject.setCheckTime(new Date());
		this.tbListenerObjectDao.save(listenerObject);
		return this.buildVo(listenerObject);
	}

	@Transactional
	@Override
	public void cancelListenerObject(Integer id){
		TbListenerObject listenerObject = this.tbListenerObjectDao.getLOById(id) ;
		List<Integer> ruleIdList = autoRuleService.findOkRuleIdByObjectId(id);
		if (CollectionUtil.isNotEmpty(ruleIdList)){
			throw new BadRequestException("还有生效的自动化规则不能注销监听对象") ;
		}
		listenerObject.setStatus(CommonEnum.CommonStatusEnum.DEL.getCode());
		this.tbListenerObjectDao.save(listenerObject);
	}

	@Transactional
	@Override
	public void delListenerObject(Integer id) {
		TbListenerObject listenerObject = this.tbListenerObjectDao.getLOById(id) ;
		if (!CommonEnum.CommonStatusEnum.DEL.getCode().equals(listenerObject.getStatus())){
			throw new BadRequestException("非注销监听对象不能删除") ;
		}
		List<Integer> ruleIdList = autoRuleService.findRuleIdByObjectId(id);
		if (CollectionUtil.isNotEmpty(ruleIdList)){
			throw new BadRequestException("还有生效的自动化规则不能删除监听对象") ;
		}
		this.listenerEventService.delAllByObjectId(id);
		this.tbListenerObjectDao.delete(listenerObject);
	}

	@Override
	public ListenerObjectVo getListenerObject(Integer id) {
		TbListenerObject listenerObject = this.tbListenerObjectDao.getLOById(id) ;
		ListenerObjectVo objectVo = this.buildVo(listenerObject);
		objectVo.setListenerEventVoList(this.listenerEventService.findAllByObjectId(id));
		return objectVo;
	}

	@Override
	public List<ListenerObjectVo> queryList(SysRequest<ListenerObjectDto> ListenerObjRequest) {
		Pageable pageable = ListenerObjRequest.pageable();
		Page<TbListenerObject> page ;
		if (ListenerObjRequest.getParamDto()!=null){
			Specification<TbListenerObject> specification = new Specification<TbListenerObject>() {
				@Override
				@Nullable
				public Predicate toPredicate(@NonNull Root<TbListenerObject> root, @NonNull CriteriaQuery<?> criteriaQuery, @NonNull CriteriaBuilder criteriaBuilder) {
					List<Predicate> predicateList = new ArrayList<>();
					Path<String> statusPath = root.get("status");
					predicateList.add(criteriaBuilder.equal(statusPath, CommonEnum.CommonStatusEnum.OK.getCode()));
					if (StrUtil.isNotBlank(ListenerObjRequest.getParamDto().getRelationType())) {
						Path<String> relationTypePath = root.get("relationType");
						predicateList.add(criteriaBuilder.equal(relationTypePath, ListenerObjRequest.getParamDto().getRelationType()));
					}
					if (StrUtil.isNotBlank(ListenerObjRequest.getParamDto().getListenerObjectName())) {
						Path<String> namePath = root.get("listenerObjectName");
						predicateList.add(criteriaBuilder.like(namePath, "%" + ListenerObjRequest.getParamDto().getListenerObjectName() + "%"));
					}
					criteriaQuery.where(predicateList.toArray(new Predicate[0]));
					return criteriaQuery.getRestriction();
				}
			};
			page = this.tbListenerObjectDao.findAll(specification,pageable);
		} else {
			page = this.tbListenerObjectDao.findAll(pageable);
		}
		List<ListenerObjectVo> objectVoList = new ArrayList<>();
		for (TbListenerObject listenerObject : page.getContent()){
			objectVoList.add(this.buildVo(listenerObject));
		}
		return objectVoList;
	}
}
